#include "Simulator.h"
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <sstream>
#include <string>
#include <vector>
using namespace std;



// Break a line apart into words stored in a vector of strings
vector <string> ParseLine(string &line) {
	istringstream ss;
	string word;
	vector <string> sv;

	ss.str(line);
	while (ss >> word)
		sv.push_back(word);

	return sv;
}



int main(int argc, char **argv) {
	int i;
	Scheduler * sched;
	Simulator * sim;
	string prompt, s;
	unsigned long timeSliceSize;
	vector <unsigned long> timeSliceSizes;
	vector <string> sv;
	bool preemptBlocksEnabled = false, traceModeEnabled = false;
	unsigned long procSwitchCycles = 0;

	// Get the prompt if there is one
	if (argc > 2) {
		cerr << "usage: " << argv[0] << " [prompt]" << endl;
		exit(EXIT_FAILURE);
	}
	else if (argc == 2) {
		prompt = argv[1];
		prompt += " ";
	}

	sim = new Simulator();
	while (true) {
		
		// Print out the prompt and then attempt to read in the line
		cout << prompt;
		if (!getline(cin, s))
			break;
		
		// Parse the line and ignore if it is empty
		sv = ParseLine(s);
		if (sv.empty())
			continue;

		// LOAD_SCRIPT: load the process script into the Simulator
		if (sv[0] == "LOAD_SCRIPT") {
			if (sv.size() != 2) {
				cout << "LOAD_SCRIPT filename" << endl;
				continue;	
			}
			sim->LoadScript(sv[1].c_str());
		// RUN: run the Simulator with the specified Scheduler
		} else if (sv[0] == "RUN") {
			if (sv.size() == 1) {
				cout << "RUN scheduler [parameters]" << endl;
				continue;	
			}
			
			// First In First Out
			if (sv[1] == "FIFO") {
				if (sv.size() != 2) {
					cout << "RUN FIFO" << endl;
					continue;
				}
				sched = new FIFO();
			// Round Robin
			} else if (sv[1] == "RR") {
				if (sv.size() != 3) {
					cout << "RUN RR timeSliceSize" << endl;
					continue;
				}
				if ((sscanf(sv[2].c_str(), "%lu", &timeSliceSize) != 1) || (timeSliceSize == 0)) {
					cout << "Parameter timeSliceSize must be positive integer" << endl;
					continue;
				}
				sched = new RR(timeSliceSize);
			// Shortest Job First
			} else if (sv[1] == "SJF") {
				if (sv.size() != 2) {
					cout << "RUN SJF" << endl;
					continue;
				}
				sched = new SJF();
			// Priority Queue
			} else if (sv[1] == "PQ") {
				if (sv.size() != 2) {
					cout << "RUN PQ" << endl;
					continue;
				}
				sched = new PQ();
			// Earliest Deadline First
			} else if (sv[1] == "EDF") {
				if (sv.size() != 2) {
					cout << "RUN EDF" << endl;
					continue;
				}
				sched = new EDF();
			// Multi-Level Feedback Queue
			} else if (sv[1] == "MLFQ") {
				if (sv.size() < 3) {
					cout << "RUN MLFQ timeSliceSize1 timeSliceSize2 ... timeSliceSizeN" << endl;
					continue;
				}
				timeSliceSizes.clear();
				for (i = 2; i < sv.size(); ++i) {
					if ((sscanf(sv[i].c_str(), "%lu", &timeSliceSize) != 1) || (timeSliceSize == 0)) {
						cout << "Parameter timeSliceSize" << i-1 << " must be positive integer" << endl;
						break;
					}
					timeSliceSizes.push_back(timeSliceSize);
				}
				if (timeSliceSizes.size() != (sv.size() - 2))
					continue;
				sched = new MLFQ(timeSliceSizes);
			// Bad Scheduler
			} else {
				cout << "Bad scheduler" << endl;
				continue;	
			}

			// Run the simulation with the desired Scheduler
			cout << "Running";
			for (i = 1; i < sv.size(); ++i) {
				cout << " " << sv[i];
			}
			cout << endl;
			sim->Run(sched, traceModeEnabled, preemptBlocksEnabled, procSwitchCycles);
			delete sched;
		// SET_PARAMS: set the Simulator parameters
		} else if (sv[0] == "SET_PARAMS") {
			if (sv.size() != 4) {
				cout << "SET_PARAMS traceModeEnabled preemptBlocksEnabled procSwitchCycles " << endl;
				continue;
			}

			// Get the traceModeEnabled parameter
			if (sv[1] == "true") {
				traceModeEnabled = true;
			} else if (sv[1] == "false") {
				traceModeEnabled = false;
			} else {
				cout << "Invalid value (" << sv[1] << ") for traceModeEnabled" << endl;
				cout << "Valid values are: 'true' or 'false'" << endl;
			}

			// Get the preemptBlocksEnabled parameter
			if (sv[2] == "true") {
				preemptBlocksEnabled = true;
			} else if (sv[2] == "false") {
				preemptBlocksEnabled = false;
			} else {
				cout << "Invalid value (" << sv[2] << ") for preemptBlocksEnabled" << endl;
				cout << "Valid values are: 'true' or 'false'" << endl;
			}
			
			// Get the procSwitchCycles parameter
			if ((sscanf(sv[3].c_str(), "%lu", &procSwitchCycles) != 1)){
				cout << "Invalid value (" << sv[3] << ") for procSwitchCycles. " << endl;
				cout << "Valid values are: >= 0" << endl;
				procSwitchCycles = 0;
			}
		// HELP: print out a list of supported commands
		} else if (sv[0] == "HELP") {
			if (sv.size() != 1) {
				cout << "HELP takes no arguments" << endl;
				continue;	
			}

			cout << "Commands are: LOAD_SCRIPT, SET_PARAMS, RUN, HELP" << endl;
		// If command is unrecognized, advise user to use HELP command
		} else {
			cout << "Bad command, type HELP to see a list of commands" << endl;
		}
	}

	return 0;
}
